package com.example.gokchinesefoodmapmcdev.config;

import com.example.gokchinesefoodmapmcdev.filter.CustomSecurityMetadataSource;
import com.example.gokchinesefoodmapmcdev.filter.JWTTokenAuthenticationFilter;
import com.example.gokchinesefoodmapmcdev.filter.ValidateCodeFilter;
import com.example.gokchinesefoodmapmcdev.handler.MyAccessDeniedHandler;
import com.example.gokchinesefoodmapmcdev.handler.MyAuthenticationEntryPoint;
import com.example.gokchinesefoodmapmcdev.handler.MyLoginFailureHandler;
import com.example.gokchinesefoodmapmcdev.handler.MyLoginSuccessHandler;
import com.example.gokchinesefoodmapmcdev.service.MyUserDetailService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.UrlAuthorizationConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

@Configuration
public class MySpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private MyUserDetailService myUserDetailService;

    @Autowired
    private MyLoginSuccessHandler myLoginSuccessHandler;

    @Autowired
    private MyLoginFailureHandler myLoginFailureHandler;

    @Autowired
    private CustomSecurityMetadataSource customSecurityMetadataSource;

    @Autowired
    private JWTTokenAuthenticationFilter jwtTokenAuthenticationFilter;

    @Autowired
    private ValidateCodeFilter validateCodeFilter;

    @Autowired
    private MyAuthenticationEntryPoint myAuthenticationEntryPoint;

    @Autowired
    private MyAccessDeniedHandler myAccessDeniedHandler;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //自定义用户认证
        auth.userDetailsService(myUserDetailService);
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        // 放过静态资源拦截
        web.ignoring().antMatchers("/css/**", "/js/**", "/images/**");
        // 放过登录页面
        web.ignoring().antMatchers("/toLoginPage", "/captcha", "/toFailPage", "/rest/app/register");
        // 放过发送验证码的请求 和 短信验证的请求
        web.ignoring().antMatchers("/rest/sms/sendMsg", "/rest/app/authCode");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 允许跨域访问（corsConfigurationSource()为配置跨域请求范围的方法，在本配置类最后）
        http.cors().configurationSource(corsConfigurationSource());

        // 将JWT token过滤器添加在用户验证过滤器之前
        http.addFilterBefore(jwtTokenAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);

        // 添加自定义验证码过滤器，在UsernamePasswordAuthenticationFilter之前
        http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class);

        // 使用自定义的权限验证规则
        ApplicationContext app = http.getSharedObject(ApplicationContext.class);
        http
                .apply(new UrlAuthorizationConfigurer<>(app))
                .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                    @Override
                    public <O extends FilterSecurityInterceptor> O postProcess(O object) {
                        object.setSecurityMetadataSource(customSecurityMetadataSource);
                        object.setRejectPublicInvocations(true);
                        return object;
                    }
                });

        // 添加自定义处理器
        http
                // 1. 开启表单验证
                .formLogin()
                // 设置自定义登录页
                .loginPage("/toLoginPage")
                // 登录请求地址
                .loginProcessingUrl("/rest/app/authPassword")
                // 2. 设置自定义登录后成功处理器
                .successHandler(myLoginSuccessHandler)
                // 设置自定义登录失败处理器
                .failureHandler(myLoginFailureHandler)
                .and()
                // 异常处理
                .exceptionHandling()
                .authenticationEntryPoint(myAuthenticationEntryPoint)
                .accessDeniedHandler(myAccessDeniedHandler)
                .and()
                // 3. 获取授权管理控制对象， Security所有的权限控制都基于这个类进行控制。
                .authorizeRequests()
                // 对所有请求
                .anyRequest()
                // 进行验证
                .authenticated();

        // 禁用session
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        // 关闭csrf防护
        http.csrf().disable();

        // 允许iframe加载页面
        http.headers().frameOptions().sameOrigin();

    }

    /**
     * 配置跨域请求操作
     *
     * @return
     */
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        // 设置允许跨域的站点，注意不能设置为*
        corsConfiguration.addAllowedOrigin("http://127.0.0.1:8080");
        // 设置允许跨域的http方法（*所有）
        corsConfiguration.addAllowedMethod("*");
        // 设置允许跨域的请求头（*所有）
        corsConfiguration.addAllowedHeader("*");
        // 允许带凭证
        corsConfiguration.setAllowCredentials(true);

        // 对所有url生效
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", corsConfiguration);
        return source;
    }
}
